package business

import (
	"encoding/json"
	"fcs-api/src/app/model"
	"fcs-api/src/client/kubernetes"
	"fmt"
	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	extentionsv1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"strings"
)

// Ingress rules 中必需有一个域名，防止用户的自定义域名全部删除后 Ingress 无法更新
var exampleIngressRule = networkingv1.IngressRule{
	Host:             "example.fc",
	IngressRuleValue: networkingv1.IngressRuleValue{},
}

// 创建并获取账号对应的唯一 Ingress cname-<accountId>，并创建路由规则
func CreateDomainIngress(fcClient *kubernetes.FcClient, domainInfo *model.Domain) error {
	namespace := model.Base.GatewayNamespace
	ingressName := getDomainIngressName(domainInfo)
	ingressRuleValue, err := GetIngressRuleValue(domainInfo)
	if err != nil {
		return err
	}
	domainRules := networkingv1.IngressRule{
		Host:             domainInfo.DomainName,
		IngressRuleValue: ingressRuleValue,
	}

	// 如果使用了 HTTPS，需要先创建证书
	var ingressTls *networkingv1.IngressTLS
	if strings.Contains(domainInfo.Protocol, model.ProtocolHTTPS) {
		tlsSecret, err := CreateTlsSecret(fcClient, domainInfo)
		if err != nil {
			_ = logger.Error("CreateDomainIngress CreateTlsSecret error %s", err.Error())
			return err
		}
		ingressTls = &networkingv1.IngressTLS{
			Hosts: []string{
				domainInfo.DomainName,
			},
			SecretName: tlsSecret.Name,
		}
	}

	// 先查询 Ingress 是否已经创建
	ingress, err := fcClient.GetIngress(namespace, ingressName)
	if err != nil {
		if errors.IsNotFound(err) {
			// Ingress 不存在，需创建
			// 同时创建 rules
			pluginName := fmt.Sprintf("%s,%s,%s", model.KCPCredential, model.KCPBss, model.KCPCorsCname)
			ingressNew := &networkingv1.Ingress{
				ObjectMeta: extentionsv1beta1.ObjectMeta{
					Name: ingressName,
					Annotations: map[string]string{
						"konghq.com/plugins":          pluginName,
						"konghq.com/preserve-host":    "false",
						"kubernetes.io/ingress.class": "kong",
					},
				},
				Spec: networkingv1.IngressSpec{
					Rules: []networkingv1.IngressRule{
						exampleIngressRule, // 占位使用
						domainRules,
					},
				},
			}
			// HTTPS 功能
			if ingressTls != nil {
				ingressNew.Spec.TLS = []networkingv1.IngressTLS{
					*ingressTls,
				}
			}
			_, err := fcClient.CreateIngress(namespace, ingressNew)
			return err
		} else {
			return err
		}
	}

	// Ingress 存在，添加 rules
	// 同时支持创建和更新接口的调用请求
	// 1. 路由规则
	var rulesNew []networkingv1.IngressRule
	for _, rule := range ingress.Spec.Rules {
		if rule.Host != domainRules.Host {
			rulesNew = append(rulesNew, rule)
		}
	}
	rulesNew = append(rulesNew, domainRules)
	ingress.Spec.Rules = rulesNew
	// 2. HTTPS 功能
	var tlsNew []networkingv1.IngressTLS
	if ingress.Spec.TLS != nil {
		for _, tls := range ingress.Spec.TLS {
			if tls.Hosts[0] != domainRules.Host {
				tlsNew = append(tlsNew, tls)
			}
		}
	}
	if ingressTls != nil {
		tlsNew = append(tlsNew, *ingressTls)
	}
	// 从 HTTP,HTTPS 切换到 HTTP，需要删除对应的 Secret
	if ingress.Spec.TLS != nil && len(ingress.Spec.TLS) > len(tlsNew) {
		logger.Debug("CreateDomainIngress: switch protocol from HTTP,HTTPS to HTTP, delete secret")
		_ = DeleteTlsSecret(fcClient, domainInfo)
	}
	if len(tlsNew) > 0 {
		ingress.Spec.TLS = tlsNew
	} else {
		ingress.Spec.TLS = nil
	}

	_, err = fcClient.UpdateIngress(namespace, ingress)
	return err
}

func DeleteDomainIngress(fcClient *kubernetes.FcClient, domainInfo *model.Domain) error {
	namespace := model.Base.GatewayNamespace
	ingressName := getDomainIngressName(domainInfo)
	// 删除 Ingress 路由规则
	ingress, err := fcClient.GetIngress(namespace, ingressName)
	if err != nil {
		_ = logger.Error("DeleteDomainIngress GetIngress error %s", err.Error())
		return err
	}
	var rulesNew []networkingv1.IngressRule
	for _, rule := range ingress.Spec.Rules {
		if rule.Host != domainInfo.DomainName {
			rulesNew = append(rulesNew, rule)
		}
	}
	ingress.Spec.Rules = rulesNew
	// 删除 Ingress Tls 配置
	var tlsNew []networkingv1.IngressTLS
	if strings.Contains(domainInfo.Protocol, model.ProtocolHTTPS) && ingress.Spec.TLS != nil {
		for _, tls := range ingress.Spec.TLS {
			if tls.Hosts[0] != domainInfo.DomainName {
				tlsNew = append(tlsNew, tls)
			}
		}
	}
	if len(tlsNew) > 0 {
		ingress.Spec.TLS = tlsNew
	} else {
		ingress.Spec.TLS = nil
	}

	// 更新 Ingress
	_, err = fcClient.UpdateIngress(namespace, ingress)
	if err != nil {
		_ = logger.Error("DeleteDomainIngress UpdateIngress error %s", err.Error())
		return err
	}

	// 如果使用了 HTTPS，再删除对应的 Secret
	if strings.Contains(domainInfo.Protocol, model.ProtocolHTTPS) {
		_ = DeleteTlsSecret(fcClient, domainInfo)
	}

	return nil
}

func getDomainIngressName(domainInfo *model.Domain) string {
	return fmt.Sprintf("cname-%s", domainInfo.A5tId)
}

func CreateTlsSecret(fcClient *kubernetes.FcClient, domainInfo *model.Domain) (*corev1.Secret, error) {
	namespace := model.Base.GatewayNamespace
	secretName := GetTlsSecretName(domainInfo)
	secret := &corev1.Secret{
		ObjectMeta: extentionsv1beta1.ObjectMeta{
			Name: secretName,
			Labels: map[string]string{
				"domain-name": domainInfo.DomainName,
				"account-id":  domainInfo.A5tId,
			},
		},
		Data: map[string][]byte{
			corev1.TLSCertKey:       []byte(domainInfo.Cert.Certificate),
			corev1.TLSPrivateKeyKey: []byte(domainInfo.Cert.Key),
		},
		Type: corev1.SecretTypeTLS,
	}
	secretCreated, err := fcClient.CreateSecret(namespace, secret)
	if err != nil && errors.IsAlreadyExists(err) {
		err := DeleteTlsSecret(fcClient, domainInfo)
		if err != nil {
			return &corev1.Secret{}, err
		}
		secretCreated, err := fcClient.CreateSecret(namespace, secret)
		return secretCreated, err
	}
	return secretCreated, err
}

func DeleteTlsSecret(fcClient *kubernetes.FcClient, domainInfo *model.Domain) error {
	namespace := model.Base.GatewayNamespace
	secretName := GetTlsSecretName(domainInfo)
	err := fcClient.DeleteSecret(namespace, secretName)
	return err
}

func GetTlsSecretName(domainInfo *model.Domain) string {
	return fmt.Sprintf("tls-%s", domainInfo.Cert.Id)
}

func GetIngressRuleValue(domainInfo *model.Domain) (networkingv1.IngressRuleValue, error) {
	var ingressRuleValue networkingv1.IngressRuleValue
	// 如果没设置路由规则，直接返回空对象
	if domainInfo.RouteConfig == "" || domainInfo.RouteConfig == "[]" {
		return ingressRuleValue, nil
	}

	// 解析路由规则的 JSON 字符串，获取路由规则数组
	var routeConfigList []*model.DomainIngressRouteConfig
	err := json.Unmarshal([]byte(domainInfo.RouteConfig), &routeConfigList)
	if err != nil {
		_ = logger.Error("getIngressRuleValue error %s", err.Error())
		return ingressRuleValue, err
	}
	// 根据路由规则创建 HTTPIngressPath 数组
	var paths []networkingv1.HTTPIngressPath
	pathTypePrefix := networkingv1.PathTypePrefix
	for _, routeConfig := range routeConfigList {
		routeConfig.ServiceName = getHttpTriggerServiceName(&model.Trigger{
			A5tId:      domainInfo.A5tId,
			FuncName:   routeConfig.FunctionName,
			GroupName:  routeConfig.GroupName,
			TargetName: routeConfig.TargetName,
			TargetType: routeConfig.TargetType,
		})
		paths = append(paths, networkingv1.HTTPIngressPath{
			PathType: &pathTypePrefix,
			Path:     routeConfig.Path,
			Backend: networkingv1.IngressBackend{
				Service: &networkingv1.IngressServiceBackend{
					Name: routeConfig.ServiceName,
					Port: networkingv1.ServiceBackendPort{
						Number: 80,
					},
				},
			},
		})
	}

	ingressRuleValue.HTTP = &networkingv1.HTTPIngressRuleValue{
		Paths: paths,
	}

	return ingressRuleValue, nil
}
